//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

#include <windows.h>
#include <minshell.h>
#include <hshell.h>
#include <pwinuser.h>

#define WM_SYSTEMSTARTED ( WM_USER + 1 )
    
// the hwnd of the currently activated window and the taskbar window 
HWND                g_hwndActivated = NULL;
HWND                g_hwndTaskBar = NULL;

static DWORD        g_dwThreadId = 0;
static HANDLE       g_hThread = NULL;

extern HWND         g_hwndDesktop;
extern HINSTANCE    g_hInstance;


// Window proc for task bar
static LRESULT TaskBar_WindowProc( 
    HWND hWnd, 
    UINT uMsg, 
    WPARAM wParam, 
    LPARAM lParam 
    )
{
    switch( uMsg )
    {
    case WM_SHELLNOTIFY:
        //
        // workaround for two problems that prevents activated/deactivated 
        // windows from receiving messages particularly in managed code. We will 
        // send an extra WM_NULL to make sure the activation messages are received 
        // and acted upon in a timely manner
        //
        if( SHELLNOTIFY_WINDOWACTIVATED == wParam )
        {
            if( g_hwndActivated != NULL )
            {
                // this window will be deactivated, make sure it got its message
                ::PostMessage( g_hwndActivated, WM_NULL, 0, 0 );
            }
            g_hwndActivated = (HWND)lParam;
            if( g_hwndActivated != NULL )
            {
                // this window will be activated, make sure it got its message
                ::PostMessage( g_hwndActivated, WM_NULL, 0, 0 );
            }

            // If the new window isn't the desktop window, tickle the idle timer to extend it
            if ( g_hwndActivated != g_hwndDesktop && NULL != g_hwndDesktop )
            {
                // Make sure gwes is in busy mode if application starts from cesh
                mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MOVE, 0, 0, 0, 0);
            }
        }
        Desktop_ShellNotificationHook( (DWORD)wParam, (LPVOID)lParam );
        break;
    case WM_POWERBROADCAST:
        SendMessage( g_hwndDesktop, uMsg, wParam, lParam );
        break;
    case WM_SYSTEMSTARTED:
        break;
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

static BOOL TaskBar_CreateWindow()
{
    static const WCHAR* kszClassName = L"HHTaskBar";
    WNDCLASS wc = { 0 };

    wc.style = CS_HREDRAW|CS_VREDRAW;
    wc.lpfnWndProc = TaskBar_WindowProc;
    wc.hInstance = g_hInstance;
    wc.lpszClassName = kszClassName;
    RegisterClass( &wc );

    g_hwndTaskBar = ::CreateWindowEx(
        0,
        kszClassName, 
        L"TaskBar",
        WS_OVERLAPPED|WS_EX_NOACTIVATE,
        0,0, 0, 0,
        NULL, 
        NULL, 
        g_hInstance, 
        0 
    );
    return g_hwndTaskBar ? TRUE : FALSE;
}

// Task bar message pump
static DWORD TaskBar_Thread(
    HANDLE hEvent
    )
{
    do
    {
        SetThreadPriority( (HANDLE)GetCurrentThreadId(), THREAD_PRIORITY_ABOVE_NORMAL );

        if( !TaskBar_CreateWindow() )
            break;

        //
        // This GWE API registers both the desktop & taskbar windows with GWE.
        // Both windows should be created before this API is called.
        // The Taskbar HWND is passed in explicitly. The Desktop HWND is located
        // from it's window-class, which *must* be "DesktopExplorerWindow"
        //
        // GWE remembers these window handles as special. The desktop window
        // is used by GWE to hide "minimized" windows (since CE doesn't support
        // true minimization, "minimized"windows just get moved behind the desktop.
        //
        // The taskbar window is notified when top-level windows are created or
        // deleted (so it can put up taskbar icons etc if it wants to -- which
        // this sample doesn't yet do). The taskbar window is also sent messages on
        // special Windows key sequences such as Alt-Tab, Ctrl-Esc etc.
        //
        if( !RegisterTaskBar( g_hwndTaskBar ) )
            break;

        ShowWindow( g_hwndTaskBar, SW_HIDE );

        // signal that the taskbar initialisation has completed
        SetEvent( hEvent );

        MSG msg;
        while( GetMessage(&msg, NULL, 0, 0) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    while( FALSE );

    if( IsWindow( g_hwndTaskBar ) )
    {
        DestroyWindow( g_hwndTaskBar );
        g_hwndTaskBar = NULL;
    }
    RegisterTaskBar( NULL );

    return 0;
}

BOOL TaskBar_Start()
{
    BOOL fSuccess = FALSE;
    HANDLE hInitEvent = NULL;

    ASSERT( !g_hThread );
    do
    {
        hInitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
        if( !hInitEvent )
            break;

        // Creates a new thread for taskbar windows
        g_hThread = CreateThread(
            NULL, 
            0, 
            ( LPTHREAD_START_ROUTINE )TaskBar_Thread, 
            ( LPVOID )hInitEvent, 
            0,
            &g_dwThreadId
        );

        if( !g_hThread )
            break;

        // we need to wait for the shell PSLs to load
        HANDLE  handles[2];
        handles[0] = hInitEvent;
        handles[1] = g_hThread;
        if( WAIT_OBJECT_0 != WaitForMultipleObjects( 2, handles, FALSE, 10000 ) )
            break;

        fSuccess = TRUE;
    }
    while( FALSE );

    if( hInitEvent )
        CloseHandle( hInitEvent );
    if( !fSuccess && g_hThread )
    {
        CloseHandle( g_hThread );
        g_hThread = NULL;
    }
    return fSuccess;
}

void TaskBar_OnSystemStarted()
{
    // Called by psl when api is ready...
    PostMessage( g_hwndTaskBar, WM_SYSTEMSTARTED, 0, 0 );
}

void TaskBar_Stop()
{
    if( g_hThread )
    {
        PostThreadMessage( g_dwThreadId, WM_QUIT, 0, 0 );
        WaitForSingleObject( g_hThread, 10000 );
        
        CloseHandle( g_hThread );
        g_hThread = NULL;
    }
}
